package main.Tests;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import main.FileVisitors.MyFileVisitor;
import main.FileVisitors.FileVisitorImpl.DriveRootDirectories;

public abstract class Test {
    private static final Path outFilePath = Paths.get("C://concurrency.log");
    private static final int WAIT_FOR_TERMINATION_SECONDS = 90;
    private static final FileSystem fs = FileSystems.getDefault();

    protected String walkOnDrive;
    protected String searchWithNoRegistry = "";
    protected int numberOfTests = 1;
    protected int maxThreadsPerTest = 1;
    protected static int startActiveThreads = 1;

    protected final DriveRootDirectories rootDirectories = new DriveRootDirectories();

    public abstract void runTest() throws InterruptedException, IOException;

    protected void runInitRootDirectories(String walkOnDrive) throws IOException {
        Path walkPath = null;

        final Iterator<Path> iter = fs.getRootDirectories().iterator();
        while (iter.hasNext()) {
            final Path path = iter.next();
            if (path.toString().equals(walkOnDrive)) {
                walkPath = path;
            }
        }
        if (walkPath == null) {
            throw new FileNotFoundException();
        }
        Files.walkFileTree(walkPath, EnumSet.noneOf(FileVisitOption.class), 2, this.rootDirectories);
        this.rootDirectories.getRootDirectories().remove(0);
    }

    protected void printHeader(int numberOfTests) throws IOException {
        final Date date = new Date();
        print(String.format("Test started at %s.%n", date.toString()));
        print(String.format("Total iterations: %d.%n", numberOfTests));
    }

    protected void printFooter() throws IOException {
        final Date date = new Date();
        print(String.format("Test finished at %s%n", date.toString()));
        print("---------------------------------------------\n");
    }

    protected static void serviceShutdown(final ExecutorService service) throws InterruptedException {
        service.shutdown();
        if (!service.awaitTermination(WAIT_FOR_TERMINATION_SECONDS, TimeUnit.SECONDS)) {
            service.shutdownNow();
            System.err.println("Service abnormal termination!");
        }
    }

    protected void print(final String output) throws IOException {
        try {
            final BufferedWriter bw = Files.newBufferedWriter(outFilePath, Charset.forName("UTF8"),
                    StandardOpenOption.APPEND);
            bw.write(output);
            bw.close();
        } catch (NoSuchFileException e) {
            Files.createFile(outFilePath);
            print(output);
        }
    }

    protected String resultToString(final MyFileVisitor visitor, final long time, String walkOnDrive) {
        final String result = "On drive: %s. Files: %d. Files size: %d b. Folders: %d. Time elapsed: %d ms. Max active threads: %d.%n";
        final Object resultArr[] = { walkOnDrive, visitor.getFiles(), visitor.getTotalSize(), visitor.getFolders(),
                time, startActiveThreads };

        final StringBuffer buffer = new StringBuffer();
        buffer.append(String.format(result, resultArr));

        buffer.append("Also found ").append(visitor.getMatches().size() + " matches:\n");
        if (visitor.getMatches().size() > 100) {
            buffer.append("...\n");
        } else {
            for (Path path : visitor.getMatches()) {
                buffer.append(path.toString() + "\n");
            }
        }
        return buffer.toString();
    }
}
